﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Innovation
{
    public class Material : Component
    {
        Effect effect = new BasicEffect(Engine.GraphicsDevice, null);
        string effectName;
        IEModel model;
        ModelMeshPart meshPart;
        Dictionary<string, object> paramsToSet = new Dictionary<string, object>();

        public Effect EffectValue
        {
            get { return effect; }
        }

        public virtual string Effect
        {
            get { return effectName; }
            set 
            { 
                effectName = value; 
                effect = Engine.Content.Load<Effect>(value).Clone(Engine.GraphicsDevice); 
                
                if (MeshPart != null) 
                    MeshPart.Effect = effect; 
            }
        }

        public ModelMeshPart MeshPart
        {
            get { return meshPart; }
        }

        public IEModel ModelValue
        {
            get { return model; }
        }

        internal virtual void setMeshPart(IEModel Model, ModelMeshPart MeshPart)
        {
            this.meshPart = MeshPart;
            this.model = Model;

            if (effect != null)
                meshPart.Effect = effect;
        }

        public static Dictionary<ModelMeshPart, T> SetupModel<T>(IEModel Model) where T : Material, new()
        {
            Dictionary<ModelMeshPart, T> output = new Dictionary<ModelMeshPart, T>();

            foreach (ModelMesh mesh in Model.ModelValue.Meshes)
            {
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    T material = new T();
                    ((Material)material).setMeshPart(Model, part);
                    output.Add(part, material);
                }
            }

            return output;
        }

        public override void Update()
        {
            if (effect != null)
            {
                if (this.meshPart.Effect != effect)
                    this.meshPart.Effect = effect;

                if (paramsToSet.Count > 0)
                {
                    foreach (KeyValuePair<string, object> value in paramsToSet)
                        SetParameter(value.Key, value.Value);

                    paramsToSet.Clear();
                }
            }
        }

        public override void SaveSerializationData(SerializationData Data)
        {
            base.SaveSerializationData(Data);

            Data.AddData("Material.Model", this.model.Name);
            Data.AddData("Material.MeshPart", ((string[])this.meshPart.Tag)[0]);
            Data.AddData("Material.Effect", (this.effectName != null ? effectName : "NULL"));
        }

        public override void LoadFromSerializationData(SerializationData Data)
        {
            base.LoadFromSerializationData(Data);

            string modelName = Data.GetData<string>("Material.Model");
            string meshPartName = Data.GetData<string>("Material.MeshPart");

            foreach(GameScreen gameScreen in Engine.GameScreens)
                foreach (Component component in gameScreen.Components)
                    if (component.Name == modelName)
                    {
                        IEModel model = (IEModel)component;

                        foreach(ModelMesh mesh in model.ModelValue.Meshes)
                            foreach (ModelMeshPart part in mesh.MeshParts)
                                if (((string[])part.Tag)[0] == meshPartName)
                                {
                                    if (!model.Materials.ContainsKey(part))
                                        model.Materials.Add(part, this);

                                        Type t = typeof(Engine);

                                    setMeshPart(model, part);
                                }

                        break;
                    }

            string effectName = Data.GetData<string>("Material.Effect");

            if (effectName != "NULL")
                this.Effect = effectName;
        }

        public void SetParameter(string Name, object Value)
        {
            Type t = Value.GetType();

            if (effect != null)
            {
                if (t == typeof(bool))
                    effect.Parameters[Name].SetValue((bool)Value);
                else if (t == typeof(bool[]))
                    effect.Parameters[Name].SetValue((bool[])Value);
                else if (t == typeof(float))
                    effect.Parameters[Name].SetValue((float)Value);
                else if (t == typeof(float[]))
                    effect.Parameters[Name].SetValue((float[])Value);
                else if (t == typeof(int))
                    effect.Parameters[Name].SetValue((int)Value);
                else if (t == typeof(int[]))
                    effect.Parameters[Name].SetValue((int[])Value);
                else if (t == typeof(Matrix))
                    effect.Parameters[Name].SetValue((Matrix)Value);
                else if (t == typeof(Matrix[]))
                    effect.Parameters[Name].SetValue((Matrix[])Value);
                else if (t == typeof(Quaternion))
                    effect.Parameters[Name].SetValue((Quaternion)Value);
                else if (t == typeof(Quaternion[]))
                    effect.Parameters[Name].SetValue((Quaternion[])Value);
                else if (t == typeof(string))
                    effect.Parameters[Name].SetValue((string)Value);
                else if (t == typeof(Texture))
                    effect.Parameters[Name].SetValue((Texture)Value);
                else if (t == typeof(Texture2D))
                    effect.Parameters[Name].SetValue((Texture2D)Value);
                else if (t == typeof(Texture3D))
                    effect.Parameters[Name].SetValue((Texture3D)Value);
                else if (t == typeof(TextureCube))
                    effect.Parameters[Name].SetValue((TextureCube)Value);
                else if (t == typeof(Vector2))
                    effect.Parameters[Name].SetValue((Vector2)Value);
                else if (t == typeof(Vector2[]))
                    effect.Parameters[Name].SetValue((Vector2[])Value);
                else if (t == typeof(Vector3))
                    effect.Parameters[Name].SetValue((Vector3)Value);
                else if (t == typeof(Vector3[]))
                    effect.Parameters[Name].SetValue((Vector3[])Value);
                else if (t == typeof(Vector4))
                    effect.Parameters[Name].SetValue((Vector4)Value);
                else if (t == typeof(Vector4[]))
                    effect.Parameters[Name].SetValue((Vector4[])Value);
            }
            else
                paramsToSet.Add(Name, Value);
        }

        public object GetParameterValue(string Name, Type T)
        {
            if (effect != null)
            {
                if (T == typeof(bool))
                    return effect.Parameters[Name].GetValueBoolean();
                else if (T == typeof(float))
                    return effect.Parameters[Name].GetValueSingle();
                else if (T == typeof(int))
                    return effect.Parameters[Name].GetValueInt32();
                else if (T == typeof(Matrix))
                    return effect.Parameters[Name].GetValueMatrix();
                else if (T == typeof(Quaternion))
                    return effect.Parameters[Name].GetValueQuaternion();
                else if (T == typeof(string))
                    return effect.Parameters[Name].GetValueString();
                else if (T == typeof(Vector2))
                    return effect.Parameters[Name].GetValueVector2();
                else if (T == typeof(Vector3))
                    return effect.Parameters[Name].GetValueVector3();
                else if (T == typeof(Vector4))
                    return effect.Parameters[Name].GetValueVector4();
                else if (T == typeof(Texture2D))
                    return effect.Parameters[Name].GetValueTexture2D();
                else if (T == typeof(Texture3D))
                    return effect.Parameters[Name].GetValueTexture3D();
                else if (T == typeof(TextureCube))
                    return effect.Parameters[Name].GetValueTextureCube();
            }

            return null;
        }

        public object GetArrayParameterValue(string Name, Type T, int Count)
        {
            if (effect != null)
            {
                if (T == typeof(bool[]))
                    return effect.Parameters[Name].GetValueBooleanArray(Count);
                else if (T == typeof(float[]))
                    return effect.Parameters[Name].GetValueSingleArray(Count);
                else if (T == typeof(int[]))
                    return effect.Parameters[Name].GetValueInt32Array(Count);
                else if (T == typeof(Matrix[]))
                    return effect.Parameters[Name].GetValueMatrixArray(Count);
                else if (T == typeof(Quaternion[]))
                    return effect.Parameters[Name].GetValueQuaternionArray(Count);
                else if (T == typeof(Vector2[]))
                    return effect.Parameters[Name].GetValueVector2Array(Count);
                else if (T == typeof(Vector3[]))
                    return effect.Parameters[Name].GetValueVector3Array(Count);
                else if (T == typeof(Vector4[]))
                    return effect.Parameters[Name].GetValueVector4Array(Count);
            }

            return null;
        }
    }
}
